#ifndef __MY__PROTOCOL__
#define __MY__PROTOCOL__
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <cstdlib>
#include <algorithm>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> //open接口
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h> //在内核区拷贝文件
#include "Util.hpp"
#include "Log.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        break;
    }
    return desc;
}

static std::string Suffix2Decs(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}};
    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        return iter->second;
    }
    return "text/html";
}

class HttpRequest
{
public:
    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank;
    std::string _request_body;

    // 解析完毕后的结果
    std::string _method;
    std::string _url; // path?args
    std::string _version;

    std::unordered_map<std::string, std::string> _head_kv;
    int content_length;
    std::string _path;
    std::string _suffix; // 后缀
    std::string _query_string;

    bool _cgi;
    int _size; // 大小放在请求报文

public:
    HttpRequest() : content_length(OK), _cgi(false)
    {
    }

    ~HttpRequest() {};
};

class HttpResponse
{
public:
    std::string _status_line;
    std::vector<std::string> _reponse_header;
    std::string _blank;
    std::string _reponse_body;

    int _status_code;
    int _fd; // 要打开的文件的fd（发送静态网页用的文件）

public:
    HttpResponse() : _blank(LINE_END), _status_code(OK), _fd(-1) {}
    ~HttpResponse() {};
};

// 读取请求，分析请求，构建响应
// IO通信
class EndPonit
{
private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;

    bool _stop;

private:
    bool RecvHttpRequestLine()
    {
        std::string line;
        if (Util::ReadLine(_sock, &line) > 0)
        {
            line.resize(line.size() - 1);
            _http_request._request_line = line;
            LOG(INFO, _http_request._request_line);
        }
        else
        {
            _stop = true;
        }
        return _stop;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (1)
        {
            line.clear();
            if(Util::ReadLine(_sock, &line)<=0){
                _stop = true;
                break;
            };
            if (line == "\n") // 说明此时报头读完了
            {
                _http_request._blank = line;
                break;
            }

            line.resize(line.size() - 1); // 这步是为了不要最后一个\n
            _http_request._request_header.push_back(line);
            LOG(INFO, line);
        }

        return _stop;
    }

    void ParseHttpRequestLine()
    {
        auto &line = _http_request._request_line; // 第一行
        std::stringstream ss(line);
        ss >> _http_request._method >> _http_request._url >> _http_request._version;
        auto &method = _http_request._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }

    void ParseHttpRequestHeader()
    {
        for (auto &iter : _http_request._request_header)
        {
            std::string key;
            std::string value;
            if (Util::CutString(iter, &key, &value, SEP))
            {
                // _http_request._head_kv[key] = value;
                _http_request._head_kv.insert({key, value});
            }
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = _http_request._method;
        if (method == "POST")
        {
            auto &header_kv = _http_request._head_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end()) // 找到了
            {
                LOG(INFO, "Post Method, Content-Length: " + iter->second);
                _http_request.content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }

    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = _http_request.content_length;
            auto &body = _http_request._request_body;

            // 读取
            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(_sock, &ch, 1, 0); // 阻塞式读取
                if (s > 0)                          // 读取成功
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                {
                    _stop = true;
                    break;
                }
            }
            LOG(WARNING, body);
        }
        return _stop;
    }

    int ProcessNonCgi()
    {
        // 文件内容
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY); // 只读方式打开
        if (_http_response._fd >= 0)                                      // 打开成功才能构建信息
        {
            return OK;
        }
        return NOT_FOUND;
    }

    int ProcessCgi()
    {
        int code = OK;

        // 父进程数据
        auto &method = _http_request._method;
        auto &query_string = _http_request._query_string; // GET
        auto &body_text = _http_request._request_body;    // POST
        auto &content_length = _http_request.content_length;
        auto &response_body = _http_response._reponse_body;

        auto &bin = _http_request._path;
        // 站在父进程角度,看读写
        int input[2];
        int output[2];

        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe output error");
            code = SERVER_ERROR;
            return code;
        }

        // 这里是新线程，但是从头到尾只有一个进程，就是httpserver
        pid_t pid = fork();
        if (pid == 0)
        { // child

            close(input[0]);
            close(output[1]);
            // exec*
            // 站在子进程角度
            // input[1]: 写出 -> 1 ->input[1]
            // output[0]:读入 -> 0 ->output[0]
            dup2(output[0], 0);
            dup2(input[1], 1);

            method_env = "METHOD=";
            method_env += method;
            putenv(&method_env[0]);

            if (method == "GET")
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv(&query_string_env[0]); // 加入环境变量
                // LOG(INFO, "Get Method, Add Query_String Env");    //这里放开会导致，父进程read也会读到这个，导致content-length不一样
            }
            else if (method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv(&content_length_env[0]);
                // LOG(INFO, "Post Method, Add Content_Length Env");
            }
            else
            {
                // do nothing
            }

            // 替换成功后，目标子进程如何知道，对应的读写文件描述符是多少呢？
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid < 0)
        { // error
            LOG(ERROR, "forl error!");
            code = SERVER_ERROR;
            return code;
        }
        else
        { // parent
            close(input[1]);
            close(output[0]);
            if (method == "POST")
            {
                LOG(INFO, "CGI POST METHOD");
                const char *start = body_text.c_str();
                int total = 0, size = 0;
                while ((size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                }
            }
            char ch = 0;
            int count = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
                // std::cerr<<count++<<std::endl;
            }
            // // LOG(INFO, "00000000");
            // std::cerr << "------------------------------------" << std::endl;
            // std::cerr << response_body << std::endl;
            // std::cerr << response_body.size() << std::endl;

            int status = 0;
            pid_t ret = waitpid(pid, &status, 0); // 阻塞式等待就可以，因为这个是线程
            if (ret == pid)
            { // 成功

                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    // std::cerr<<"___________________________________"<<std::endl;
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    void HandlerError(std::string page)
    {
        //给用户返回404页面
        _http_response._fd = open(page.c_str(), O_RDONLY);
        if (_http_response._fd > 0)
        { // 打开成功
            struct stat st;
            stat(page.c_str(), &st);
            _http_request._size = st.st_size;
            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _http_response._reponse_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response._reponse_header.push_back(line);
        }
    }

    void BuildOkResponse()
    {
        // 正确的形式
        std::string line = "Content-Type: ";
        line += Suffix2Decs(_http_request._suffix);
        line += LINE_END;
        _http_response._reponse_header.push_back(line);
        line = "Content-Length: ";
        if (_http_request._cgi)
            line += std::to_string(_http_response._reponse_body.size());
        else
            line += std::to_string(_http_request._size); // Get

        line += LINE_END;
        _http_response._reponse_header.push_back(line);
    }

    void BuildHttpResponseHelper()
    {
        //_http_request
        //_http_response
        auto &code = _http_response._status_code;

        // 构建状态行
        auto &status_line = _http_response._status_line;
        status_line = HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;

        // 构建响应正文，可能包含响应报头
        std::string path = WEB_ROOT;
        // std::cerr<<"-------------------------------code:"<<code<<std::endl;
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path = path + "/" + PAGE_404;
            HandlerError(path); // 状态行 + 响应报头 也有了，空行也有了，正文也有了
            break;
            // case 500:
            //     HandlerError(PAGE_500);
        case BAD_REQUEST:
            path = path + "/" + PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            path = path + "/" + PAGE_404;
            HandlerError(path);
            break;
        default:
            break;
        }
    }

public:
    EndPonit(int sock) : _sock(sock), _stop(false)
    {
    }

    bool IsStop()
    {
        return _stop;
    }
    void RecvHttpRequest()
    {
        //短路求值  false 为没问题
        if((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
        // if(RecvHttpRequestLine()||RecvHttpRequestHeader())
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            RecvHttpRequestBody();
        }
        
    }

    void BuildHttpResponse()
    {
        std::string path;
        struct stat st;
        std::size_t found = 0;
        auto &code = _http_response._status_code;
        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
            // 非法请求

            LOG(WARNING, "method is not right...");
            code = BAD_REQUEST;

            goto END;
        }
        if (_http_request._method == "GET") // 这种情况才进行url的参数分析
        {
            size_t pos = _http_request._url.find("?");
            if (pos != std::string::npos)
            {
                // 找到了
                Util::CutString(_http_request._url, &_http_request._path, &_http_request._query_string, "?");
                _http_request._cgi = true;
            }
            else
            {
                // 没抄到
                _http_request._path = _http_request._url;
            }
        }
        else if (_http_request._method == "POST")
        {
            // POST
            _http_request._cgi = true;
            _http_request._path = _http_request._url;
        }
        else
        {
            // DO NOTHING
        }

        // 此时就已经获得了请求的资源路径path /a/b/c
        // 为了它在服务器上找到这个资源
        // 需要再拼接服务器的数据路径   WEB_ROOT/a/b/c
        path = _http_request._path; // 正常来说GOTO之间不能有临时变量，会影响编译器的初始化（有可能会越过这个临时变量）
        _http_request._path = WEB_ROOT;
        _http_request._path += path;

        if (_http_request._path[_http_request._path.size() - 1] == '/') // 如果path的最后一个是/说明访问的目录，我们不能把目录给他，应该给他根目录
        {
            _http_request._path += HOME_PAGE;
        }

        // 现在我们获取了资源路径了，如何知道路径是否存在呢？
        //  使用stat  系统调用函数，获取文件属性

        if (stat(_http_request._path.c_str(), &st) == 0) // 获取成功
        {
            // 资源存在
            if (S_ISDIR(st.st_mode))
            {
                // 说明请求的是一个目录，那么请求是不被允许的，需要做一下相关处理
                // 虽然是一个目录，但是绝对不会以/结尾！
                _http_request._path += "/";
                _http_request._path += WEB_ROOT;
                stat(_http_request._path.c_str(), &st); // 因为我们要给他发送另一个网页（首页），需要重新设置st
            }
            // 看看是不是有可执行权限（有可能需要访问，可执行的文件）
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 特殊处理
                _http_request._cgi = true;
            }
            _http_request._size = st.st_size;
        }
        else
        {
            // 资源不存在
            LOG(WARNING, _http_request._path + "Not Found!");
            code = NOT_FOUND;
            goto END;
        }
        // path一定存在 ， 在这里提取后缀
        found = path.rfind(".");
        if (found == std::string::npos) // 没找到
        {
            _http_request._suffix = ".html";
        }
        else
        {
            _http_request._suffix = _http_request._path.substr(found);
        }

        if (_http_request._cgi == true)
        {
            code = ProcessCgi(); // 获取用户的发送的数据，构建CGI模式
            // 执行目标程序，拿到结果放在http_response_body里面
        }
        else
        {
            // 1.到这里，目标网页一定存在
            // 2.返回并不是单单返回网页，而是构建HTTP响应
            code = ProcessNonCgi(); // 简单的网页返回，返回静态网页，只需要把网页打开即可
        }

    END:
        // if (code != OK)
        // {
        BuildHttpResponseHelper();
        // }
        return;
    }

    void SendHttpResponse()
    {
        auto &code = _http_response._status_code;
        // 发送请求行+报头
        // 为什么调用这么多次send？为啥不一次发？
        // 不麻烦，因为我们只是把数据发从到--发送缓冲区（用户层拷贝到内核层）--，具体什么时候发送由TCP协议决定

        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0); // 请求行
        for (auto &iter : _http_response._reponse_header)                                        // 报头
        {
            send(_sock, iter.c_str(), iter.size(), 0);
        }

        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0); // 空行
        // fd , resposnse_body
        // 正文(为什么不用send)，因为该系统调用是内核之间的拷贝，效率更高，不经过用户层
        if (_http_request._cgi && code == OK)
        {
            auto &response_body = _http_response._reponse_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = response_body.c_str();
            while (total < response_body.size() && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
            // std::cerr<<"+++++++++++++++++++++++"<<std::endl;
        }
        else
        {
            // std::cout<<"=========================="<<std::endl;
            sendfile(_sock, _http_response._fd, nullptr, _http_request._size);
            close(_http_response._fd);
        }
    }

    ~EndPonit()
    {
    }
};

// #define DEBUG 1

// class EntraCnce
class CallBack
{
public:
    CallBack()
    {}

    void operator()(int sock)
    {
        HandlerRequest(sock);
    }

    void HandlerRequest(int sock)
    {
        LOG(INFO, "Hander Request Begin....");
        // int sock = *(int *)_sock;
        // delete (int *)_sock;

//         std::cout << "get a new link ... : " << sock << std::endl;
#ifdef DEBUG
        // FOR Test
        char buffer[4096];
        recv(sock, buffer, sizeof buffer, 0); // 阻塞方式区读

        std::cout << "--------------start--------------\n";
        std::cout << buffer << std::endl;
        std::cout << "--------------end----------------\n";

#else
        EndPonit *ep = new EndPonit(sock);
        ep->RecvHttpRequest();
        if(!ep->IsStop()){
            LOG(INFO, "Recv No Error, Begin Build and Send");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else{
            LOG(WARNING, "Recv Error , Stop Build and Send");
        }

        delete ep;
#endif

        // std::string line;
        // int len = Util::ReadLine(sock, &line);

        // std::cout<<line<<std::endl;

        close(sock); // 服务完把sock关上了，很正常！！！
        LOG(INFO, "Hander Request End");
        return;
    }


    ~CallBack(){}
};

#endif